<template>
  <div class="d3-graph-container">
    <div class="graph-controls">
      <button @click="resetView" class="control-btn">重置视图</button>
      <button @click="togglePause" class="control-btn">
        {{ isPaused ? '继续' : '暂停' }}
      </button>
      <div class="info-panel">
        <span>节点: {{ graphNodes.length }}</span>
        <span>连线: {{ graphLinks.length }}</span>
      </div>
    </div>
    <div ref="graphContainer" class="graph-svg-container"></div>
  </div>
</template>

<script>
import * as d3 from 'd3'

export default {
  name: 'SimpleD3Graph',
  props: {
    // 支持直接传递data对象
    data: {
      type: Object,
      default: () => ({ nodes: [], links: [] })
    },
    // 支持分别传递nodes和links
    nodes: {
      type: Array,
      default: () => []
    },
    links: {
      type: Array,
      default: () => []
    },
    width: {
      type: Number,
      default: 800
    },
    height: {
      type: Number,
      default: 600
    }
  },
  data() {
    return {
      svg: null,
      simulation: null,
      isPaused: false,
      zoom: null,
      nodeGroup: null,
      linkGroup: null,
      labelGroup: null,
      isUpdating: false,
      updateTimer: null
    }
  },
  computed: {
    // 统一的数据源，优先使用data属性，否则使用单独的nodes和links
    graphNodes() {
      let nodes = []
      if (this.data && this.data.nodes) {
        nodes = this.data.nodes
      } else {
        nodes = this.nodes || []
      }
      
      // 确保每个节点都有必要的属性
      return nodes.map((node, index) => {
        // 如果节点是字符串，转换为对象
        if (typeof node === 'string') {
          return {
            id: node,
            name: node,
            x: Math.random() * this.width,
            y: Math.random() * this.height
          }
        }
        
        // 确保节点有id
        if (!node.id) {
          node.id = node.name || `node-${index}`
        }
        
        // 确保节点有坐标
        if (typeof node.x !== 'number') {
          node.x = Math.random() * this.width
        }
        if (typeof node.y !== 'number') {
          node.y = Math.random() * this.height
        }
        
        return node
      })
    },
    graphLinks() {
      let links = []
      if (this.data && this.data.links) {
        links = this.data.links
      } else {
        links = this.links || []
      }
      
      // 确保连线有正确的格式
      return links.map(link => {
        // 如果连线是字符串数组，转换为对象
        if (Array.isArray(link) && link.length >= 2) {
          return {
            source: link[0],
            target: link[1],
            id: `${link[0]}-${link[1]}`
          }
        }
        
        // 确保连线有id
        if (!link.id) {
          link.id = `${link.source}-${link.target}`
        }
        
        return link
      })
    }
  },
  mounted() {
    console.log('SimpleD3Graph mounted with data:', {
      data: this.data,
      nodes: this.nodes,
      links: this.links,
      computedNodes: this.graphNodes,
      computedLinks: this.graphLinks
    })
    this.initGraph()
    this.startSimulation()
  },
  beforeUnmount() {
    this.cleanup()
  },
  watch: {
    // 监听计算属性
    graphNodes: {
      handler() {
        this.scheduleUpdate()
      },
      deep: true
    },
    graphLinks: {
      handler() {
        this.scheduleUpdate()
      },
      deep: true
    },
    // 保持对原始props的监听以支持向后兼容
    nodes: {
      handler() {
        this.scheduleUpdate()
      },
      deep: true
    },
    links: {
      handler() {
        this.scheduleUpdate()
      },
      deep: true
    },
    data: {
      handler() {
        this.scheduleUpdate()
      },
      deep: true
    }
  },
  methods: {
    initGraph() {
      // 清理现有内容
      if (this.svg) {
        this.svg.remove()
      }

      // 创建SVG容器
      this.svg = d3.select(this.$refs.graphContainer)
        .append('svg')
        .attr('width', this.width)
        .attr('height', this.height)
        .style('background', '#1a1a1a')
        .style('border-radius', '8px')

      // 创建缩放行为
      this.zoom = d3.zoom()
        .scaleExtent([0.1, 4])
        .on('zoom', (event) => {
          this.nodeGroup.attr('transform', event.transform)
          this.linkGroup.attr('transform', event.transform)
          this.labelGroup.attr('transform', event.transform)
        })

      this.svg.call(this.zoom)

      // 创建分组
      this.linkGroup = this.svg.append('g').attr('class', 'links')
      this.nodeGroup = this.svg.append('g').attr('class', 'nodes')
      this.labelGroup = this.svg.append('g').attr('class', 'labels')

      // 初始化力导向图
      this.initSimulation()
    },

    initSimulation() {
      // 创建力导向图模拟
      this.simulation = d3.forceSimulation(this.graphNodes)
        .force('link', d3.forceLink(this.graphLinks)
          .id(d => d.id)
          .distance(150) // 连接距离
          .strength(0.1) // 连接强度
        )
        .force('charge', d3.forceManyBody()
          .strength(-800) // 节点间斥力
        )
        .force('center', d3.forceCenter(this.width / 2, this.height / 2)
          .strength(0.1) // 中心力
        )
        .force('collision', d3.forceCollide()
          .radius(d => this.getNodeRadius(d) + 10) // 碰撞检测半径
        )
        .force('x', d3.forceX(this.width / 2).strength(0.05))
        .force('y', d3.forceY(this.height / 2).strength(0.05))

      // 设置模拟参数
      this.simulation
        .alpha(0.3)
        .alphaDecay(0.01)
        .velocityDecay(0.4)
        .restart()

      // 绑定tick事件
      this.simulation.on('tick', () => {
        this.updatePositions()
      })
    },

    startSimulation() {
      if (this.simulation) {
        this.simulation.restart()
      }
    },

    scheduleUpdate() {
      // 使用防抖来避免频繁更新
      if (this.updateTimer) {
        clearTimeout(this.updateTimer)
      }
      
      this.updateTimer = setTimeout(() => {
        this.updateGraph()
        this.updateTimer = null
      }, 100)
    },

    updateGraph() {
      if (!this.svg || !this.simulation || this.isUpdating) return

      console.log('Updating graph with:', {
        nodes: this.graphNodes.length,
        links: this.graphLinks.length,
        nodesData: this.graphNodes.slice(0, 3), // 只显示前3个节点
        linksData: this.graphLinks.slice(0, 3),  // 只显示前3个连线
        svgExists: !!this.svg,
        simulationExists: !!this.simulation,
        nodeGroupExists: !!this.nodeGroup,
        linkGroupExists: !!this.linkGroup
      })

      this.isUpdating = true

      try {
        // 更新数据
        this.simulation.nodes(this.graphNodes)
        this.simulation.force('link').links(this.graphLinks)

        // 重新渲染
        this.renderLinks()
        this.renderNodes()
        this.renderLabels()

        // 重启模拟
        this.simulation.alpha(0.3).restart()
        
        console.log('Graph update completed successfully')
      } catch (error) {
        console.error('Error updating graph:', error)
      } finally {
        this.isUpdating = false
      }
    },

    renderLinks() {
      const link = this.linkGroup.selectAll('line')
        .data(this.graphLinks, d => `${d.source.id}-${d.target.id}`)

      // 移除旧连线
      link.exit().remove()

      // 添加新连线
      const linkEnter = link.enter()
        .append('line')
        .attr('stroke', '#666')
        .attr('stroke-width', 2)
        .attr('stroke-opacity', 0.6)

      // 合并新旧连线
      link.merge(linkEnter)
        .attr('x1', d => d.source.x)
        .attr('y1', d => d.source.y)
        .attr('x2', d => d.target.x)
        .attr('y2', d => d.target.y)
    },

    renderNodes() {
      console.log('Rendering nodes:', {
        nodeCount: this.graphNodes.length,
        nodeGroup: this.nodeGroup,
        firstNode: this.graphNodes[0]
      })
      
      const node = this.nodeGroup.selectAll('circle')
        .data(this.graphNodes, d => d.id)

      console.log('Node selection:', {
        existing: node.size(),
        entering: node.enter().size(),
        exiting: node.exit().size()
      })

      // 移除旧节点
      node.exit().remove()

      // 添加新节点
      const nodeEnter = node.enter()
        .append('circle')
        .attr('r', d => this.getNodeRadius(d))
        .attr('fill', d => this.getNodeColor(d))
        .attr('stroke', '#fff')
        .attr('stroke-width', 2)
        .style('cursor', 'pointer')
        .call(this.dragBehavior())

      // 合并新旧节点
      const nodeMerged = node.merge(nodeEnter)
        .attr('cx', d => d.x)
        .attr('cy', d => d.y)

      console.log('Final node count:', nodeMerged.size())

      // 添加交互事件
      this.addNodeInteractions(nodeMerged)
    },

    renderLabels() {
      const label = this.labelGroup.selectAll('text')
        .data(this.graphNodes, d => d.id)

      // 移除旧标签
      label.exit().remove()

      // 添加新标签
      const labelEnter = label.enter()
        .append('text')
        .attr('text-anchor', 'middle')
        .attr('dy', '0.35em')
        .attr('font-size', '12px')
        .attr('font-family', 'Arial, sans-serif')
        .attr('fill', '#fff')
        .style('pointer-events', 'none')
        .text(d => d.name || d.id)

      // 合并新旧标签
      label.merge(labelEnter)
        .attr('x', d => d.x)
        .attr('y', d => d.y)
    },

    updatePositions() {
      // 防止在更新过程中触发位置更新
      if (this.isUpdating) return

      // 更新连线位置
      this.linkGroup.selectAll('line')
        .attr('x1', d => d.source.x)
        .attr('y1', d => d.source.y)
        .attr('x2', d => d.target.x)
        .attr('y2', d => d.target.y)

      // 更新节点位置
      this.nodeGroup.selectAll('circle')
        .attr('cx', d => d.x)
        .attr('cy', d => d.y)

      // 更新标签位置
      this.labelGroup.selectAll('text')
        .attr('x', d => d.x)
        .attr('y', d => d.y)
    },

    dragBehavior() {
      return d3.drag()
        .on('start', (event, d) => {
          if (!event.active) this.simulation.alphaTarget(0.3).restart()
          d.fx = d.x
          d.fy = d.y
          
          // 视觉反馈
          d3.select(event.sourceEvent.target)
            .transition()
            .duration(100)
            .attr('r', this.getNodeRadius(d) + 5)
            .style('opacity', 1)
        })
        .on('drag', (event, d) => {
          d.fx = event.x
          d.fy = event.y
        })
        .on('end', (event, d) => {
          if (!event.active) this.simulation.alphaTarget(0)
          d.fx = null
          d.fy = null
          
          // 恢复视觉状态
          d3.select(event.sourceEvent.target)
            .transition()
            .duration(200)
            .attr('r', this.getNodeRadius(d))
            .style('opacity', 0.9)
        })
    },

    addNodeInteractions(nodeSelection) {
      nodeSelection
        .on('click', (event, d) => {
          console.log('Node clicked:', d)
          this.$emit('node-click', d)
        })
        .on('mouseover', (event, d) => {
          // 高亮当前节点
          d3.select(event.currentTarget)
            .transition()
            .duration(200)
            .attr('r', this.getNodeRadius(d) + 8)
            .style('opacity', 1)

          // 高亮相关连线
          this.linkGroup.selectAll('line')
            .style('opacity', l => 
              l.source === d || l.target === d ? 1 : 0.2
            )
        })
        .on('mouseout', (event, d) => {
          // 恢复节点状态
          d3.select(event.currentTarget)
            .transition()
            .duration(200)
            .attr('r', this.getNodeRadius(d))
            .style('opacity', 0.9)

          // 恢复连线状态
          this.linkGroup.selectAll('line')
            .style('opacity', 0.6)
        })
    },

    getNodeRadius(d) {
      // 根据节点大小或类型计算半径
      if (d.size) {
        return Math.max(Math.sqrt(d.size) * 2, 8)
      }
      return 12
    },

    getNodeColor(d) {
      // 根据节点类型或属性返回颜色
      if (d.type) {
        const colors = {
          'person': '#4CAF50',
          'company': '#2196F3',
          'location': '#FF9800',
          'event': '#9C27B0',
          'default': '#607D8B'
        }
        return colors[d.type] || colors.default
      }
      return '#607D8B'
    },

    resetView() {
      // 重置缩放
      this.svg.transition()
        .duration(750)
        .call(this.zoom.transform, d3.zoomIdentity)

      // 重新随机化节点位置
      this.graphNodes.forEach(node => {
        node.x = Math.random() * this.width
        node.y = Math.random() * this.height
        node.fx = null
        node.fy = null
      })

      // 重启模拟
      this.simulation.alpha(0.5).restart()
    },

    togglePause() {
      this.isPaused = !this.isPaused
      if (this.isPaused) {
        this.simulation.stop()
      } else {
        this.simulation.alpha(0.3).restart()
      }
    },

    cleanup() {
      if (this.updateTimer) {
        clearTimeout(this.updateTimer)
        this.updateTimer = null
      }
      if (this.simulation) {
        this.simulation.stop()
      }
      if (this.svg) {
        this.svg.remove()
      }
    }
  }
}
</script>

<style scoped>
.d3-graph-container {
  width: 100%;
  height: 100%;
  position: relative;
  background: #1a1a1a;
  border-radius: 8px;
  overflow: hidden;
}

.graph-controls {
  position: absolute;
  top: 10px;
  left: 10px;
  z-index: 10;
  display: flex;
  gap: 10px;
  align-items: center;
}

.control-btn {
  padding: 8px 16px;
  background: rgba(255, 255, 255, 0.1);
  border: 1px solid rgba(255, 255, 255, 0.2);
  border-radius: 4px;
  color: white;
  cursor: pointer;
  font-size: 12px;
  transition: all 0.2s ease;
}

.control-btn:hover {
  background: rgba(255, 255, 255, 0.2);
  border-color: rgba(255, 255, 255, 0.4);
}

.info-panel {
  display: flex;
  gap: 15px;
  color: white;
  font-size: 12px;
  opacity: 0.8;
}

.graph-svg-container {
  width: 100%;
  height: 100%;
}

/* 全局样式 */
:deep(.links line) {
  stroke: #666;
  stroke-opacity: 0.6;
}

:deep(.nodes circle) {
  stroke: #fff;
  stroke-width: 2px;
  opacity: 0.9;
}

:deep(.labels text) {
  fill: #fff;
  font-size: 12px;
  font-family: Arial, sans-serif;
  text-anchor: middle;
  pointer-events: none;
}
</style>